package com.zx.security.block.filter;


import com.zx.core.constant.TokenConstants;
import com.zx.core.context.TenantContextHolder;
import com.zx.security.block.service.ICustomTokenService;
import com.zx.security.common.model.LoginUser;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Objects;

/**
 * <p>
 * description:token校验 <br>
 * create: 2024-08-20 13:26
 * </p>
 *
 * @author zhou  xun
 */
@Component
@RequiredArgsConstructor
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    //OncePerRequestFilter主要作用是确保一个HTTP请求在处理过程中只被过滤一次，避免过滤器逻辑被重复执行，从而提高系统的性能和效率。
    private final ICustomTokenService iCustomTokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            // 1. 从请求头中获取令牌
            String token = request.getHeader(TokenConstants.AUTHENTICATION);

            // 2. 处理令牌字符串（如果存在且符合格式）
            if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {
                // 移除令牌前缀（如"Bearer "）
                token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
            }

            // 3. 根据令牌获取登录用户信息
            LoginUser loginUser = iCustomTokenService.getLoginUser(token);

            // 4. 如果用户信息存在，进行认证处理
            if (Objects.nonNull(loginUser)) {
                // 4.1 创建认证令牌对象，包含用户凭证、权限信息
                // 参数说明：principal(用户信息), credentials(凭证，这里为null), authorities(权限集合)
                UsernamePasswordAuthenticationToken authenticationToken =
                        new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());

                // 4.2 将JWT token信息添加到认证详情中，以便后续可以直接获取
                if (StringUtils.isNotEmpty(token)) {
                    // 解析JWT token并添加到认证详情中
                    // 这样可以在SecurityUtils中直接从认证信息中获取用户ID，而不需要访问Redis
                    try {
                        Jwt jwt = iCustomTokenService.getJwtDecoder().decode(token);
                        authenticationToken.setDetails(jwt);

                        // 从JWT中提取租户信息并设置到上下文
                        String tenantId = jwt.getClaimAsString("tenantId");
                        String tenantName = jwt.getClaimAsString("tenantName");

                        if (StringUtils.isNotEmpty(tenantId)) {
                            TenantContextHolder.setTenantId(tenantId);
                        }

                        if (StringUtils.isNotEmpty(tenantName)) {
                            TenantContextHolder.setTenantName(tenantName);
                        }
                    } catch (Exception e) {
                        // 解析失败时忽略
                    }
                }

                // 4.3 将认证信息存入SecurityContextHolder，供后续流程使用
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }

            // 5. 放行请求，继续过滤器链的执行
            filterChain.doFilter(request, response);
        } finally {
            // 清除租户上下文
            TenantContextHolder.clear();
        }
    }

}